﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using HtmlAgilityPack;
using IFDB.Models.Contexts;
using IFDB.Models.Tables;
using IFDB.ResourceLibrary;
using IFDB.Services.Foundation;
using IFDB.Services.Foundation.Sites.Sources;

namespace IFDB.Services.Application.Sites.Sources
{
    public class Espn : BaseImport
    {
        public Espn() : base(Site.ESPN)
        {
        }

        public delegate void UpdateConsoleEventHandler(string status);
        public event UpdateConsoleEventHandler WriteToConsole;

        private IFDBContext db = new IFDBContext();

        #region ImportResults
        public void ImportMatches(DateTime startDate, DateTime endDate, short rank)
        {
            //Fixtures
            DateTime searchDate = DateTime.Now;

            while (searchDate <= endDate)
            {
                ImportFixtures(Country.England, "eng", rank, searchDate);
                searchDate = searchDate.AddDays(29);
            }

            //Results
            searchDate = startDate.AddDays(28);

            while (searchDate <= DateTime.Now)
            {
                ImportResults(Country.England, "eng", rank, searchDate);
                searchDate = searchDate.AddDays(29);
            }

            ImportResults(Country.England, "eng", rank, endDate);
        }

        private void ImportFixtures(Country country, string countryShortName, short? rank, DateTime viewDate)
        {
            WriteToConsole("Rank: " + rank + "  Date: " + viewDate.ToShortDateString());

            string espnCompetitionId = countryShortName + "." + rank.ToString();
            string url = "http://soccernet.espn.go.com/fixtures/_/league/" + espnCompetitionId + "/date/" + viewDate.ToString("yyyyMMdd") + "?cc=5739";

            HtmlDocument document = GetHtmlDocument(url);
            HtmlNodeCollection matches = document.DocumentNode.SelectNodes("//table[@class=\"tablehead\"][tr/td[@colspan=\"7\" or @colspan=\"6\"]]");

            if (matches != null)
            {
                foreach (HtmlNode table in matches)
                {
                    String matchDateString = rank == 1 ? table.SelectSingleNode("tr/td[@colspan=\"7\"]").InnerText : table.SelectSingleNode("tr/td[@colspan=\"6\"]").InnerText;
                    DateTime matchDate = Convert.ToDateTime(matchDateString);

                    foreach (HtmlNode row in table.SelectNodes("tr[@class!=\"stathead\"][@class!=\"colhead\"]"))
                    {
                        string team1EspnId = "";
                        string team1Name = string.Empty;
                        string team2EspnId = "";
                        string team2Name = string.Empty;
                        string espnMatchId = "";
                        string matchURL = string.Empty;
                        Guid team1Id = Guid.Empty;
                        Guid team2Id = Guid.Empty;

                        for (Int32 cellCount = 0; cellCount <= row.SelectNodes("td").Count - 1; cellCount++)
                        {
                            HtmlNode cell = row.SelectNodes("td")[cellCount];
                            switch (cellCount)
                            {
                                case 0:
                                    DateTime time = DateTime.Parse(cell.InnerText.Substring(0, 5).Trim());
                                    matchDate = Convert.ToDateTime(matchDateString);
                                    matchDate = matchDate.AddHours(time.Hour).AddMinutes(time.Minute);
                                    break;

                                case 1:
                                    if (cell.SelectSingleNode("a") != null)
                                    {
                                        team1Name = cell.InnerText;
                                        HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                        team1EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                        team1Id = ProcessTeam(team1EspnId, team1Name);
                                    }
                                    break;

                                case 2:
                                    matchURL = cell.SelectSingleNode("a").Attributes["href"].Value;
                                    
                                    if (matchURL.IndexOf("/uk/en/report/") > -1)
                                        espnMatchId = matchURL.GetTextBetween("/uk/en/report/", "/report.html");
                                    else if (matchURL.IndexOf("/statistics/id/") > -1)
                                        espnMatchId = matchURL.GetTextBetween("/statistics/id/", "/statistics.html");
                                    else if (matchURL.IndexOf("/uk/en/gamecast/") > -1)
                                        espnMatchId = matchURL.GetTextBetween("/uk/en/gamecast/", "/gamecast.html");

                                    break;

                                case 3:
                                    if (cell.SelectSingleNode("a") != null)
                                    {
                                        team2Name = cell.InnerText;
                                        HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                        team2EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                        team2Id = ProcessTeam(team2EspnId, team2Name);
                                    }
                                    continue;
                            }
                        }

                        Competition competition = db.Competitions.Single(c => c.Country == country && c.Rank == rank && c.StartDate <= matchDate && c.EndDate >= matchDate);

                        Match match = ProcessMatch(MatchStatus.NotPlayed, matchDate, competition.CompetitionId, null, null, team1Id, null, null, null, null, null, team2Id, null, null, null, null, null);

                        if (match == null)
                            continue;

                        ProcessLookupMatch(espnMatchId, match.MatchId);
                        WriteToConsole("Fixture: " + matchDate.ToShortDateString() + " " + team1Name + " v " + team2Name);

                        db.SaveChanges();
                    }
                }
            }
            else
                WriteToConsole("Error");
        }

        private void ImportResults(Country country, string countryShortName, short? rank, DateTime viewDate)
        {
            WriteToConsole("Rank: " + rank + "  Date: " + viewDate.ToShortDateString());

            string espnCompetitionId = countryShortName + "." + rank.ToString();
            string url = "http://soccernet.espn.go.com/results/_/league/" + espnCompetitionId + "/date/" + viewDate.ToString("yyyyMMdd") + "?cc=5739";

            HtmlDocument document = GetHtmlDocument(url);
            HtmlNodeCollection matches = document.DocumentNode.SelectNodes("//table[@class=\"tablehead\"][tr/td[@colspan=\"7\" or @colspan=\"6\"]]");

            if (matches != null)
            {
                foreach (HtmlNode table in matches)
                {
                    String matchDateString = rank == 1 ? table.SelectSingleNode("tr/td[@colspan=\"7\"]").InnerText : table.SelectSingleNode("tr/td[@colspan=\"6\"]").InnerText;
                    DateTime matchDate = Convert.ToDateTime(matchDateString);

                    foreach (HtmlNode row in table.SelectNodes("tr[@class!=\"stathead\"][@class!=\"colhead\"]"))
                    {
                        bool fullTime = false;
                        string team1EspnId = "";
                        string team1Name = string.Empty;
                        string team2EspnId = "";
                        string team2Name = string.Empty;
                        string espnMatchId = "";
                        string matchURL = string.Empty;
                        Guid team1Id = Guid.Empty;
                        Guid team2Id = Guid.Empty;
                        short team1FT = 0;
                        short team2FT = 0;

                        for (Int32 cellCount = 0; cellCount <= row.SelectNodes("td").Count - 1; cellCount++)
                        {
                            HtmlNode cell = row.SelectNodes("td")[cellCount];
                            switch (cellCount)
                            {
                                case 0:
                                    fullTime = (cell.InnerText == "FT" || cell.InnerText == "FT-Pens" || cell.InnerText == "AET");
                                    break;

                                case 1:
                                    if (cell.SelectSingleNode("a") != null)
                                    {
                                        team1Name = cell.InnerText;
                                        HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                        team1EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                        team1Id = ProcessTeam(team1EspnId, team1Name);
                                    }
                                    break;

                                case 2:
                                    if (fullTime)
                                    {

                                        matchURL = cell.SelectSingleNode("a").Attributes["href"].Value;

                                        if (matchURL.IndexOf("/uk/en/report/") > -1)
                                            espnMatchId = matchURL.GetTextBetween("/uk/en/report/", "/report.html");
                                        else if (matchURL.IndexOf("/uk/en/gamecast/") > -1)
                                            espnMatchId = matchURL.GetTextBetween("/uk/en/gamecast/", "/gamecast.html");

                                        team1FT = Convert.ToInt16(cell.InnerText.GetTextBefore("-"));
                                        team2FT = Convert.ToInt16(cell.InnerText.GetTextAfter("-"));
                                    }
                                    break;

                                case 3:

                                    if (cell.SelectSingleNode("a") != null)
                                    {
                                        team2Name = cell.InnerText;
                                        HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                        team2EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                        team2Id = ProcessTeam(team2EspnId, team2Name);
                                    }
                                    break;
                            }
                        }

                        Competition competition = db.Competitions.Single(c => c.Country == country && c.Rank == rank && c.StartDate <= matchDate && c.EndDate >= matchDate);
                        MatchStatus matchStatus = fullTime ? MatchStatus.ScoreOnly : MatchStatus.NotPlayed;
                        Match match = ProcessMatch(matchStatus, matchDate, competition.CompetitionId, null, null, team1Id, null, team1FT, null, null, null, team2Id, null, team2FT, null, null, null);

                        if (match == null)
                            continue;
                        
                        ProcessLookupMatch(espnMatchId, match.MatchId);
                        WriteToConsole("Result: " + matchDate.ToShortDateString() + " " + team1Name + " " + team1FT + "  " + team2Name + " " + team2FT);

                        if (fullTime)
                            ImportMatchDetail(espnMatchId, match, country);
                    }

                    db.SaveChanges();
                }
            }
            else
                WriteToConsole("Error");
        }
        #endregion

        #region ImportMatchDetails
        private void ImportMatchDetail(string espnMatchId, Match match, Country defaultCountry)
        {
            if (espnMatchId.Length == 0)
                return;

            if (match.MatchStatus != MatchStatus.ScoreOnly)
                return;

            match.Team1HT = 0;
            match.Team2HT = 0;
            
            string url = "http://espnfc.com/uk/en/gamecast/statistics/id/" + espnMatchId.ToString() + "/statistics.html?soccernet=true&cc=5739";
            //Get match details
            HtmlDocument document = GetHtmlDocument(url);

            string matchDateString = document.DocumentNode.SelectNodes(@"//div[@class=""match-details""]/p")[0].InnerText;
            long jQueryDate = Convert.ToInt64(matchDateString.GetTextBetween("new Date(", ");"));
            DateTime matchDate = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(jQueryDate);

            string locationString = document.DocumentNode.SelectNodes(@"//div[@class=""match-details""]/p")[1].InnerText;
            string venueName;
            string countryName;
            
            ResolveVenue(locationString, out venueName, out countryName);

            foreach (HtmlNode header in document.DocumentNode.SelectNodes(@"//div[@class=""matchup""]"))
            {
                Guid? venueId = ProcessVenue(defaultCountry, countryName, venueName, matchDate, locationString);
                long attendance = Convert.ToInt64(header.SelectSingleNode("p/span").InnerText.Replace("Attendance:", string.Empty).Trim());

                Guid teamId = match.Team1Id;

                foreach (HtmlNode table in document.DocumentNode.SelectNodes("//table[@class=\"stat-table\"]"))
                {
                    bool isSubstitute = false;

                    foreach (HtmlNode row in table.SelectNodes("tbody/tr"))
                    {
                        if (row.SelectSingleNode("th") != null)
                        {
                            if (row.SelectSingleNode("th").Attributes["class"].Value == "tb-substitutes home-team" || row.SelectSingleNode("th").Attributes["class"].Value == "tb-substitutes away-team")
                                isSubstitute = true;
                        }

                        if (row.SelectSingleNode("td") == null || row.SelectNodes("td").Count < 2)
                            continue;

                        string position = row.SelectNodes("td")[0].InnerText;
                        string fullName = row.SelectNodes("td")[2].SelectSingleNode("a").InnerText;

                        string forenames;
                        string surname;
                        ResolvePersonNames(fullName, out forenames, out surname);

                        string working = row.SelectNodes("td")[2].SelectSingleNode("a").Attributes["href"].Value;
                        string espnPersonId = working.GetTextBetween("person/_/id/", "/");

                        Guid personId;
                        var lookupPerson = LookupPerson.Search(db, Site, espnPersonId);


                        if (lookupPerson == null)
                        {
                            Person person = Person.CreateNew(surname, forenames, null, ResolvePosition(position));
                            personId = person.PersonId;
                            db.Persons.Add(person);
                            db.LookupPersons.Add(LookupPerson.CreateNew(Site, espnPersonId, personId));

                            LookupPerson.CreateNew(Site, espnPersonId, personId);
                        }
                        else
                        {
                            personId = lookupPerson.PersonId;
                            Person.CreateNew(surname, forenames, null, ResolvePosition(position));
                        }

                        HtmlNodeCollection appearanceAttributes = row.SelectNodes("td")[2].SelectNodes("div");
                        MatchEventType startingStatus = isSubstitute
                            ? MatchEventType.Substitute
                            : MatchEventType.Started;

                        var appearanceEvent = AppearanceEvent.Search(db, match.MatchId, personId, teamId, startingStatus);

                        if (appearanceEvent == null)
                            db.AppearanceEvents.Add(AppearanceEvent.CreateNew(match.MatchId, personId, teamId, startingStatus, null, null));

                        if (appearanceAttributes != null)
                        {
                            foreach (HtmlNode appearanceAttribute in appearanceAttributes)
                            {
                                int? minute = null;
                                int? plus = null;
                                MatchEventType? matchEventType = GetMatchEvent(appearanceAttribute, out minute, out plus);

                                if (matchEventType != null)
                                {
                                    appearanceEvent = AppearanceEvent.Search(db, match.MatchId, personId, teamId, (MatchEventType)matchEventType);

                                    if (appearanceEvent == null)
                                        db.AppearanceEvents.Add(AppearanceEvent.CreateNew(match.MatchId, personId, teamId, (MatchEventType)matchEventType, minute, plus));
                                }

                                if (minute <= 45)
                                {
                                    if (matchEventType == MatchEventType.Goal && teamId == match.Team1Id || matchEventType == MatchEventType.OwnGoal && teamId == match.Team2Id)
                                        match.Team1HT++;

                                    if (matchEventType == MatchEventType.Goal && teamId == match.Team2Id || matchEventType == MatchEventType.OwnGoal && teamId == match.Team1Id)
                                        match.Team2HT++;

                                }
                            }
                        }
                    }
                
                    teamId = match.Team2Id;
                }

                ProcessMatch(MatchStatus.FullMatch, matchDate, match.CompetitionId, venueId, attendance, match.Team1Id, match.Team1HT, match.Team1FT, match.Team1ETHT, match.Team1ETFT, match.Team1ETPens, match.Team2Id, match.Team2HT, match.Team2FT, match.Team2ETHT, match.Team2ETFT, match.Team2ETPens);
                WriteToConsole("Updated");
            }
        }
        #endregion

        private Guid? ProcessVenue(Country defaultCountry, string countryName, string venueName, DateTime matchDate, string espnVenueName)
        {
            Country country;
            if (!Enum.TryParse(countryName, true, out country))
                country = defaultCountry;

            Guid venueId = Guid.Empty;

            LookupVenue lookupVenue = LookupVenue.Search(db, Site, espnVenueName);

            if (lookupVenue == null)
            {
                venueId = Venue.AddOrUpdate(db, null, country, venueName).VenueId;
                db.LookupVenues.Add(LookupVenue.CreateNew(Site, espnVenueName, venueId));
            }
            else
                venueId = lookupVenue.VenueId;

            return venueId;
        }

        private static Position ResolvePosition(string term)
        {
            switch (term)
            {
                case "G":
                    return Position.Goalkeeper;

                case "D":
                    return Position.Defender;

                case "M":
                    return Position.Midfielder;

                case "F":
                    return Position.Forward;

                default:
                    return Position.Unknown;
            }
        }

        private static void ResolvePersonNames(string fullName, out string forenames, out string surname)
        {
            forenames = string.Empty;
            surname = string.Empty;

            if (fullName.IndexOf(" ") > 0)
            {
                for (int pos = fullName.Length - 1; pos > 0; pos--)
                {
                    if (fullName.Substring(pos, 1) == " ")
                    {
                        forenames = fullName.Substring(0, pos).Trim();
                        surname = fullName.Substring(pos).Trim();
                        break;
                    }
                }
            }
            else
                surname = fullName;
        }

        private static MatchEventType? GetMatchEvent(HtmlNode appearanceAttribute, out int? minute, out int? plus)
        {
            minute = null;
            plus = null;
            string working = appearanceAttribute.Attributes["onmouseover"].Value.GetTextBetween("</strong> - ", "\\'").Trim();
            int tryNumber;

            if (int.TryParse(working, out tryNumber))
                minute = tryNumber;
            else
            {
                int plusPosition = working.IndexOf("+");

                if (plusPosition > 0)
                {
                    string lhs = working.Substring(0, plusPosition).Trim();

                    if (int.TryParse(lhs, out tryNumber))
                        minute = tryNumber;

                    string rhs = working.Substring(plusPosition + 1);

                    if (int.TryParse(rhs, out tryNumber))
                        plus = tryNumber;
                }
            }
            
            switch (appearanceAttribute.Attributes["class"].Value)
            {
                case "soccer-icons soccer-icons-redcard":
                    return MatchEventType.RedCard;

                case "soccer-icons soccer-icons-yellowcard":
                    return MatchEventType.YellowCard;

                case "soccer-icons soccer-icons-goal":
                    return MatchEventType.Goal;

                case "soccer-icons soccer-icons-owngoal":
                    return MatchEventType.OwnGoal;

                case "soccer-icons soccer-icons-subinout":
                    return MatchEventType.Substitution;

                default:
                    break;
            }

            return null;
        }

        private static void ResolveVenue(string locationString, out string venueName, out string countryName)
        {
            locationString = locationString.Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("\t", string.Empty);
            venueName = (locationString.IndexOf(",") != -1) ? locationString.Substring(0, locationString.IndexOf(",")).Trim() : locationString.Trim();
            countryName = (locationString.IndexOf(",") != -1) ? locationString.Substring(locationString.IndexOf(",") + 2) : string.Empty;
        }

        private static DateTime GetMatchDate(string dateString)
        {
            dateString = dateString.Replace("BST", "");
            return DateTime.Parse(dateString);
        }

        private Guid ProcessTeam(string espnId, string teamName)
        {
            LookupTeam lookupTeam = LookupTeam.Search(db, Site, espnId);

            if (lookupTeam == null)
            {
                //Team team = Team.CreateNew(teamName);
                //db.Teams.Add(team);
                //db.LookupTeams.Add(LookupTeam.CreateNew(Site, espnId, team.TeamId));
                return Guid.Empty;
            }
            else
                return lookupTeam.TeamId;
        }

        private Match ProcessMatch(MatchStatus matchStatus, DateTime matchDate, Guid competitionId, Guid? venueId, long? attendance, Guid team1Id, short? team1HT, short? team1FT, short? team1ETHT, short? team1ETFT, short? team1ETPens, Guid team2Id, short? team2HT, short? team2FT, short? team2ETHT, short? team2ETFT, short? team2ETPens)
        {
            Match match = Match.Search(db, matchDate, competitionId, team1Id, team2Id);

            if (match == null)
            {
                match = Match.CreateNew(matchStatus, matchDate, competitionId, venueId, attendance, team1Id, team1HT, team1FT, team1ETHT, team1ETFT, team1ETPens, team2Id, team2HT, team2FT, team2ETHT, team2ETFT, team2ETPens);
                db.Matches.Add(match);
                return match;
            }
            else if (match.MatchStatus == MatchStatus.FullMatch)
                return null;
            else
                return Match.Update(match, matchStatus, matchDate, competitionId, venueId, attendance, team1Id, team1HT, team1FT, team1ETHT, team1ETFT, team1ETPens, team2Id, team2HT, team2FT, team2ETHT, team2ETFT, team2ETPens);
        }

        private void ProcessLookupMatch(string espnLookupMatchId, Guid matchId)
        {
            LookupMatch lookpMatch = LookupMatch.Search(db, Site, espnLookupMatchId);

            if (lookpMatch == null)
            {
                var lookupMatch = LookupMatch.CreateNew(Site, espnLookupMatchId, matchId);
                db.LookupMatches.Add(lookupMatch);
            }
        }

        public void GetTeamLookups(string competition)
        {
            StreamWriter log = new StreamWriter(@"P:\logs\IFDB_ESPN_Team_Lookup.csv", true);

            string url = string.Format("http://espnfc.com/clubs/_/league/{0}/english-premier-league?cc=5739", competition);

            HtmlDocument document = GetHtmlDocument(url);

            // <a href="http://espnfc.com/team?id=359"><strong>Arsenal</strong></a>

            var teamQuery = from a in document.DocumentNode.Descendants("a")
                        where a.Attributes["href"].Value.IndexOf("http://espnfc.com/team?id=") != -1
                        && a.Descendants("strong").Count() == 1
                        select a;

            foreach (var a in teamQuery)
            {
                string teamName = a.Descendants("strong").Single().InnerText.Trim();
                string espnId = a.Attributes["href"].Value.GetTextAfter("http://espnfc.com/team?id=");

                log.WriteLine(string.Format("{0}, {1}", teamName, espnId));
            }

            log.Close();
        }
    }
}
